/**
* Copyright 2014 Daum Kakao Corp.
*
* Redistribution and modification in source or binary forms are not permitted without specific prior written permission.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kakao.http;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import android.net.Uri;
import android.os.Bundle;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kakao.helper.CommonProtocol;
import com.kakao.helper.Logger;
import com.kakao.helper.SystemInfo;
import com.kakao.helper.Utility;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.Request;
/**
* @author MJ
*/
public class HttpRequestTask<T> implements Runnable {
private static final ObjectMapper objectMapper = new ObjectMapper();
public static final int SUCCESS = 1;
public static final int ERROR = 2;
public static final int NEED_TO_LOGIN = 4;
private static AsyncHttpClientConfig asyncHttpClientConfig = defaultConfigure();
public static final AsyncHttpClient ASYNC_HTTP_CLIENT = newAsyncHttpClient(); // Todo close를 어디서 하나.
public static final HashMap<String,String> KA_HEADER = createKAHeader();
private static final int DEFAULT_MAX_REQUEST_RETRY = 1;
private static final int DEFAULT_CONNECTION_TO_IN_MS = 5000;
private static final int DEFAULT_REQUEST_TO_IN_MS = 30 * 1000;
private static final int DEFAULT_IDLE_CONNECTION_TO_IN_MS = 300 * 1000;
private static final int DEFAULT_IDLE_CONNECTION_POOL_IN_MS = 300 * 1000;
private final KakaoAsyncHandler<T> asyncHandler;
protected final Request request;
public HttpRequestTask(final Request request, final KakaoAsyncHandler<T> asyncHandler){
this.request = request;
this.asyncHandler = asyncHandler;
}
private static HashMap<String, String> createKAHeader() {
HashMap<String, String> kaHeader = new HashMap<String, String>();
kaHeader.put(CommonProtocol.KA_HEADER_KEY, SystemInfo.getKAHeader());
return kaHeader;
}
public static String createBaseURL(final String authority, final String requestPath) {
Uri uri = Utility.buildUri(authority, requestPath);
return uri.toString();
}
@Override
public void run() {
try {
preRequest();
ASYNC_HTTP_CLIENT.executeRequest(request, asyncHandler);
} catch (Exception e) {
Logger.getInstance().e(e);
failedRequest(e);
}
}
protected void preRequest() {
}
protected void failedRequest(Exception e) {
}
private static AsyncHttpClient newAsyncHttpClient() {
// return new AsyncHttpClient(new GrizzlyAsyncHttpProvider(asyncHttpClientConfig), asyncHttpClientConfig);
return new AsyncHttpClient(new SimpleAsyncHttpProvider(asyncHttpClientConfig), asyncHttpClientConfig);
}
// default configuration을 쓰고 싶지 않으면 HttpRequestTask를 처음 사용하기 전에 호출해 준다.
public static void setAsyncHttpClientConfig(final AsyncHttpClientConfig asyncHttpClientConfig) {
HttpRequestTask.asyncHttpClientConfig = asyncHttpClientConfig;
}
private static AsyncHttpClientConfig defaultConfigure() {
final AsyncHttpClientConfig.Builder configBuilder = new AsyncHttpClientConfig.Builder();
configBuilder.setAllowPoolingConnection(true);
configBuilder.setMaxRequestRetry(DEFAULT_MAX_REQUEST_RETRY);
configBuilder.setConnectionTimeoutInMs(DEFAULT_CONNECTION_TO_IN_MS);
configBuilder.setRequestTimeoutInMs(DEFAULT_REQUEST_TO_IN_MS);
configBuilder.setIdleConnectionTimeoutInMs(DEFAULT_IDLE_CONNECTION_TO_IN_MS);
configBuilder.setIdleConnectionInPoolTimeoutInMs(DEFAULT_IDLE_CONNECTION_POOL_IN_MS);
configBuilder.setExecutorService(HttpTaskManager.getHttpExecutor());
return configBuilder.build();
}
private static void destroy() {
ASYNC_HTTP_CLIENT.close();
}
/**
* String/Boolean/Number만 지원.
*/
public static void addQueryParams(BoundRequestBuilder requestBuilder, Bundle parameters) {
if(parameters == null)
return;
Set<String> keys = parameters.keySet();
for (String key : keys) {
Object value = parameters.get(key);
String valueStr = null;
if (value == null) {
value = "";
}
if (isSupportedParameterType(value)) {
valueStr = parameterToString(value);
} else {
throw new IllegalArgumentException(String.format("Unsupported parameter type for GET request: %s",
value.getClass().getSimpleName()));
}
requestBuilder.addQueryParameter(key, valueStr);
}
}
/**
* String/Boolean/Number 또는 value 타입이 이와같은 Map/Set만 지원.
*/
public static void addParams(final BoundRequestBuilder requestBuilder, final Bundle parameters) {
if(parameters == null)
return;
Set<String> keys = parameters.keySet();
for (String key : keys) {
Object value = parameters.get(key);
String valueStr = null;
if (value == null) {
value = "";
}
if (isSupportedParameterType(value)) {
valueStr = parameterToString(value);
} else if(value instanceof Map || value instanceof Set){
try {
valueStr = objectMapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(String.format("Unsupported parameter type : %s",
value.getClass().getSimpleName()));
}
} else {
throw new IllegalArgumentException(String.format("Unsupported parameter type : %s",
value.getClass().getSimpleName()));
}
requestBuilder.addParameter(key, valueStr);
}
}
private static boolean isSupportedParameterType(final Object value) {
return value instanceof String || value instanceof Boolean || value instanceof Number;
}
private static String parameterToString(final Object value) {
if (value instanceof String) {
return (String) value;
} else if (value instanceof Boolean || value instanceof Number) {
return value.toString();
}
throw new IllegalArgumentException("Unsupported parameter type.");
}
}